package com.alogic.commonmark.image;

import org.commonmark.Extension;
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.renderer.html.AttributeProviderContext;
import org.commonmark.renderer.html.AttributeProviderFactory;
import org.commonmark.renderer.html.HtmlRenderer;
import java.util.Set;

import org.commonmark.parser.delimiter.DelimiterProcessor;
import org.commonmark.parser.delimiter.DelimiterRun;

import java.util.*;

/**
 * Image属性插件
 * @since 1.6.13.11 [20200901 duanyy]
 */
public class ImageAttributesExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {

    private ImageAttributesExtension() {
    }

    public static Extension create() {
        return new ImageAttributesExtension();
    }

    @Override
    public void extend(Parser.Builder parserBuilder) {
        parserBuilder.customDelimiterProcessor(new ImageAttributesDelimiterProcessor());
    }

    @Override
    public void extend(HtmlRenderer.Builder rendererBuilder) {
        rendererBuilder.attributeProviderFactory(new AttributeProviderFactory() {
            @Override
            public AttributeProvider create(AttributeProviderContext context) {
                return ImageAttributesAttributeProvider.create();
            }
        });
    }

    public static class ImageAttributesAttributeProvider implements AttributeProvider {

        private ImageAttributesAttributeProvider() {
        }

        public static ImageAttributesAttributeProvider create() {
            return new ImageAttributesAttributeProvider();
        }

        @Override
        public void setAttributes(Node node, String tagName, final Map<String, String> attributes) {
            if (node instanceof Image) {
                node.accept(new AbstractVisitor() {
                    @Override
                    public void visit(CustomNode node) {
                        if (node instanceof ImageAttributes) {
                            ImageAttributes imageAttributes = (ImageAttributes) node;
                            for (Map.Entry<String, String> entry : imageAttributes.getAttributes().entrySet()) {
                                attributes.put(entry.getKey(), entry.getValue());
                            }
                            imageAttributes.unlink();
                        }
                    }
                });
            }
        }
    }

    public static class ImageAttributesDelimiterProcessor implements DelimiterProcessor {
        private static final Set<String> SUPPORTED_ATTRIBUTES = Collections.unmodifiableSet(
                new HashSet<>(Arrays.asList("width", "height")));

        @Override
        public char getOpeningCharacter() {
            return '{';
        }

        @Override
        public char getClosingCharacter() {
            return '}';
        }

        @Override
        public int getMinLength() {
            return 1;
        }

        @Override
        public int getDelimiterUse(DelimiterRun opener, DelimiterRun closer) {
            return 1;
        }

        @Override
        public void process(Text opener, Text closer, int delimiterCount) {
            if (opener.getPrevious() instanceof Image) {
                boolean canApply = true;
                List<Node> toUnlink = new ArrayList<>();

                Map<String, String> attributesMap = new LinkedHashMap<>();
                Node tmp = opener.getNext();
                while (tmp != null && tmp != closer) {
                    Node next = tmp.getNext();
                    // Only Text nodes can be used for attributes
                    if (tmp instanceof Text) {
                        String attributes = ((Text) tmp).getLiteral();
                        for (String s : attributes.split("\\s+")) {
                            String[] attribute = s.split("=");
                            if (attribute.length > 1 && SUPPORTED_ATTRIBUTES.contains(attribute[0].toLowerCase())) {
                                attributesMap.put(attribute[0], attribute[1]);
                                toUnlink.add(tmp);
                            } else {
                                canApply = false;
                                break;
                            }
                        }
                    } else {
                        canApply = false;
                        break;
                    }
                    tmp = next;
                }

                if (canApply) {
                    for (Node node : toUnlink) {
                        node.unlink();
                    }

                    if (attributesMap.size() > 0) {
                        ImageAttributes imageAttributes = new ImageAttributes(attributesMap);
                        Node nodeToStyle = opener.getPrevious();
                        nodeToStyle.appendChild(imageAttributes);
                    }
                    return;
                }
            }

            if (opener.getPrevious() == null) {
                opener.getParent().prependChild(new Text("" + getOpeningCharacter()));
            } else {
                opener.getPrevious().insertAfter(new Text("" + getOpeningCharacter()));
            }
            closer.insertAfter(new Text("" + getClosingCharacter()));
        }
    }
}
